﻿using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace Network
{
	public interface INetwork
	{
		bool IsHost();
		void RegistConnectedCallback(System.Action callback);
		void RegistReceiveCallback(System.Action<string> callback);
		bool IsConnected();
		void StartConnect();
		void SendMessage(string msg);
		void Close();
	}

	public class GameServer : INetwork
	{
		Socket socket;
		IPEndPoint ipe;
		Thread connectThread;
		Thread msgThread;
		Socket clientSocket;
		byte[] buffer;
		System.Action connectCallback;
		System.Action<string> receiveCallback;
		public void RegistConnectedCallback(System.Action callback) { connectCallback = callback; }
		public void RegistReceiveCallback(System.Action<string> callback) { receiveCallback = callback; }
		public bool IsConnected() { return clientSocket != null && clientSocket.Connected; } 
		public bool IsHost() { return true; }
		public GameServer(string ip, int port)
		{
			buffer = new byte[4096];
			socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			ipe = new IPEndPoint(IPAddress.Parse(ip), port);
			socket.Bind(ipe);
			socket.Listen(1); // 设置最大连接数
		}
		public void StartConnect()
		{
			connectThread = new Thread(Listening);
			connectThread.Start();
		}
		public void SendMessage(string msg)
		{
			if (IsConnected())
			{
				var data = Encoding.UTF8.GetBytes(msg);
				clientSocket?.Send(data);
			}
		}
		public void Close()
		{
			connectThread?.Abort();
			//msgThread?.Abort();
			if (socket.Connected) socket.Shutdown(SocketShutdown.Both);
			socket.Close();
			if (clientSocket != null && clientSocket.Connected) clientSocket.Shutdown(SocketShutdown.Both);
			clientSocket?.Close();
		}

		void Listening()
		{
			clientSocket = socket.Accept();
			msgThread = new Thread(ReceiveMessage);
			msgThread.Start();
			connectCallback?.Invoke();
		}
		void ReceiveMessage()
		{
			while (true)
			{
				if (IsConnected())
				{
					if (clientSocket.Available > 0)
					{
						int revCnt = clientSocket.Receive(buffer);
						string str = Encoding.UTF8.GetString(buffer, 0, revCnt);
						receiveCallback?.Invoke(str);
					}
				}
				else
				{
					break;
				}
			}
		}
	}

	public class GameClient : INetwork
	{
		Socket socket;
		IPEndPoint ipe;
		Thread connectThread;
		Thread msgThread;
		byte[] buffer;
		System.Action connectCallback;
		System.Action<string> receiveCallback;
		public void RegistConnectedCallback(System.Action callback) { connectCallback = callback; }
		public void RegistReceiveCallback(System.Action<string> callback) { receiveCallback = callback; }
		public bool IsConnected() { return socket != null && socket.Connected; }
		public bool IsHost() { return false; }
		public GameClient(string ip, int port)
		{
			buffer = new byte[4096];
			socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
			ipe = new IPEndPoint(IPAddress.Parse(ip), port);
		}
		public void StartConnect()
		{
			connectThread = new Thread(Connecting);
			connectThread.Start();
		}
		public void SendMessage(string msg)
		{
			if (IsConnected())
			{
				var data = Encoding.UTF8.GetBytes(msg);
				socket?.Send(data);
			}
		}
		public void Close()
		{
			connectThread?.Abort();
			//msgThread?.Abort();
			if (socket.Connected) socket.Shutdown(SocketShutdown.Both);
			socket.Close();
		}

		void Connecting()
		{
			socket.Connect(ipe);
			msgThread = new Thread(ReceiveMessage);
			msgThread.Start();
			connectCallback?.Invoke();
		}
		void ReceiveMessage()
		{
			while (true)
			{
				if (IsConnected())
				{
					if (socket.Available > 0)
					{
						int revCnt = socket.Receive(buffer);
						string str = Encoding.UTF8.GetString(buffer, 0, revCnt);
						receiveCallback?.Invoke(str);
					}
				}
				else
				{
					break;
				}
			}
		}
	}
}
